#pragma once
#include "requestor.hpp"
#include <unordered_set>

namespace RPC
{
    namespace client
    {
        class TopicManager
        {
        public:
            using ptr = std::shared_ptr<TopicManager>;
            using SubCallback = std::function<void(const std::string & key, const std::string& msg)>;
            TopicManager(const Requestor::ptr& requestor) : _requestor(requestor){};
            bool create(const BaseConnection::ptr& conn, std::string &key)
            {
                return commonRequest(conn, key, TopicOpType::TOPIC_CREATE);
            }

            void remove(const BaseConnection::ptr& conn,const std::string &key)
            {
                return commonRequest(conn, key, TopicOpType::TOPIC_REMOVE);
            }
            
            bool subscribe(const BaseConnection::ptr& conn, std::string &key, const SubCallback& cb)
            {
                addSubscribe(key, cb);
                bool ret = commonRequest(conn, key, TopicOpType::TOPIC_SUBSCRIBE);
                if(ret == false)
                {
                    delSubscribe(key);
                    return false;
                }
                return true;
            }

            bool cancel(const BaseConnection::ptr& conn, const std::string& key)
            {
                delSubscribe(key);
                bool ret = commonRequest(conn, key, TopicOpType::TOPIC_CANCEL);
            }
            bool publish(const BaseConnection::ptr& conn, const std::string& key, const std::string& msg)
            {
                 bool ret = commonRequest(conn, key, TopicOpType::TOPIC_PUBLISH, msg);
            }
            void onTopicRequest(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                //1. 从消息里面取出操作类型进行判断, 是否消息请求;
                auto type = msg->Optype();
                if(type != TopicOpType::TOPIC_PUBLISH)
                {
                    ELOG("收到了错误类型的主题操作!");
                    return;
                }
                //2. 取出消息主题名称和消息内容;
                std::string topic_key = msg->topicKey();
                std::string topic_msg = msg->topicMsg();
                //3. 通过主题名称, 查找对应的主题回调函数, 有就处理, 无就报错;
                auto callback = getSubscribe(topic_key);
                if(!callback) 
                {
                    ELOG("收到了 %s 主题消息, 但是该消息无主题处理回调! ", topic_key.c_str());
                    return;
                }
                return callback(topic_key, topic_msg);
            }
        
        private:
            void addSubscribe(const std::string& key, const SubCallback& cb)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _topic_callbacks.insert(std::make_pair(key, cb));
            }

            void delSubscribe(const std::string& key)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _topic_callbacks.erase(key);
            }

            const SubCallback getSubscribe(const std::string& key)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _topic_callbacks.find(key);
                if(it == _topic_callbacks.end())
                {
                    return SubCallback();
                }
                return it->second;
            }
            bool commonRequest(const BaseConnection::ptr& conn, const std::string &key, TopicOpType type, 
                        const std::string& msg = "")
            {
                //构造请求对象, 并且填充数据;
                auto msg_req =  MessageFactory::create<TopicRequest>();
                UUID  u;
                msg_req->setId(u.uuid());
                msg_req->setMType(MType::REQ_TOPIC);
                msg_req->setOptype(TopicOpType::TOPIC_CREATE);
                msg_req->setTopicKey(key);
                if(type == TopicOpType::TOPIC_PUBLISH)
                {
                    msg_req->setTopicMsg(msg);
                }
                //向服务器发送请求, 等待响应;
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, msg_req, msg_rsp);
                if(ret == false)
                {
                    ELOG("主题操作请求失败! ");
                    return false;
                }
                //判断请求是否成功;
                auto topic_rsp_msg = std::dynamic_pointer_cast<TopicResponse>(msg_rsp);
                if(!topic_rsp_msg)
                {
                    ELOG("主题响应, 向下类型转换失败! ");
                    return false;
                }
                if(topic_rsp_msg->rcode() != RCode::RCODE_OK)
                {
                    ELOG("主题请求出错: %s", errReason(topic_rsp_msg->rcode()));
                    return false;
                }
            }
        private:
            std::mutex _mutex;
            std::unordered_map<std::string, SubCallback> _topic_callbacks;
            Requestor::ptr _requestor;
        };
    }
}